﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using Kindohm.KSynth.Library;

namespace Kindohm.KSynth.UI
{
    public partial class MainPage : UserControl
    {
        int yCount = 20;
        Sequencer sequencer;
        Dictionary<int, ISampleMaker> rowVoices;
        Dictionary<int, ISampleMaker> drumRowVoices;
        bool playing;

        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            this.rowVoices = new Dictionary<int, ISampleMaker>();
            this.drumRowVoices = new Dictionary<int, ISampleMaker>();
            this.sequencer = new Sequencer();

            this.noteGrid.GridNoteChanged += new EventHandler<GridNoteChangedEventArgs>(noteGrid_GridNoteChanged);
            this.noteGrid.Steps = 64;
            this.drumGrid.GridNoteChanged += new EventHandler<GridNoteChangedEventArgs>(drumGrid_GridNoteChanged);
            this.drumGrid.Steps = 64;

            int octaves = 6;
            int inc = 0;

            IWaveForm form = new SawWaveForm();

            for (int oct = octaves; oct >= 0; oct--)
            {
                for (int note = 11; note >= 0; note--)
                {
                    Pitch pitch = new Pitch(note, oct);

                    Oscillator osc1 = new Oscillator();
                    osc1.WaveForm = form;
                    Oscillator osc2 = new Oscillator();
                    osc2.WaveForm = form;
                    Oscillator osc3 = new Oscillator();
                    osc3.WaveForm = form;
                    Oscillator osc4 = new Oscillator();
                    osc4.WaveForm = form;
                    Oscillator osc5 = new Oscillator();
                    osc5.WaveForm = form;
                    Oscillator osc6 = new Oscillator();
                    osc6.WaveForm = form;
                    Oscillator osc7 = new Oscillator();
                    osc7.WaveForm = form;
                    Oscillator osc8 = new Oscillator();
                    osc8.WaveForm = form;
                    Oscillator osc9 = new Oscillator();
                    osc9.WaveForm = form;
                    Oscillator osc10 = new Oscillator();
                    osc10.WaveForm = form;

                    Voice voice = new Voice();
                    voice.Attenuation = 0;
                    voice.Oscillators.AddRange(new List<Oscillator>() { osc1, osc2, osc3, osc4 });

                    voice.Frequency = pitch.Frequency;
                    this.rowVoices.Add(inc, voice);
                    inc++;
                }
            }


            yCount = inc + 1;
            this.noteGrid.Notes = yCount;
            this.sequencer.VoiceCount = 2;

            this.LoadDrums();

            SynthMediaStreamSource source = new SynthMediaStreamSource(44100, 2);
            source.SampleMaker = this.sequencer;
            this.media.SetSource(source);
            this.media.Stop();
            this.tempoBox_ValueChanged(this.tempoBox, null);
            this.stepBox_ValueChanged(this.stepBox, null);
            this.texture.Value = 1;
            this.texture.Minimum = 1;
            this.space.Value = 100;
        }

        void drumGrid_GridNoteChanged(object sender, GridNoteChangedEventArgs e)
        {
            ISampleMaker voice = this.drumRowVoices[e.Row];
            if (e.New)
            {
                this.sequencer.AddNote(voice, e.Column, e.Length);
            }
            else if (e.New == false & e.Deleted == false)
            {
                this.sequencer.ModifyNote(voice, e.Column, e.Length);
            }
            else if (e.Deleted)
            {
                this.sequencer.DeleteNote(voice, e.Column);
            }
        }

        void SetLabels()
        {
            this.drumGrid.SetLabelText(4, "Toc");
            this.drumGrid.SetLabelText(3, "Tic");
            this.drumGrid.SetLabelText(2, "Hat");
            this.drumGrid.SetLabelText(1, "Sn");
            this.drumGrid.SetLabelText(0, "Kick");
        }

        byte[] GetResource(string fileName)
        {
            Uri uri = new Uri("Kindohm.KSynth.UI;component/" + fileName, UriKind.Relative);

            byte[] buffer;
            StreamResourceInfo info = App.GetResourceStream(uri);
            using (Stream stream = info.Stream)
            {
                buffer = new byte[stream.Length];
                stream.Read(buffer, 0, (int)stream.Length);

            }
            return buffer;
        }

        void ResetSequencers()
        {
            this.sequencer.StepCount = (int)this.stepBox.Value;
            this.sequencer.Reset();
        }

        void noteGrid_GridNoteChanged(object sender, GridNoteChangedEventArgs e)
        {
            ISampleMaker voice = this.rowVoices[e.Row];
            if (e.New)
            {
                this.sequencer.AddNote(voice, e.Column, e.Length);
            }
            else if (e.New == false & e.Deleted == false)
            {
                this.sequencer.ModifyNote(voice, e.Column, e.Length);
            }
            else if (e.Deleted)
            {
                this.sequencer.DeleteNote(voice, e.Column);
            }
        }

      

        private void funky_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker sampleMaker = this.rowVoices[key];
                if (sampleMaker is Voice)
                {
                    double funkyScale = funky.Value / 100;
                    Voice voice = (Voice)sampleMaker;
                    double scaleIncrement = 10;

                    voice.Oscillators[0].Detune = 0;
                    voice.Oscillators[2].Detune = scaleIncrement * (funkyScale);

                    voice.Oscillators[1].Detune = 3 * scaleIncrement * (-funkyScale);
                    voice.Oscillators[3].Detune = 2 * scaleIncrement * (-funkyScale);

                    foreach (Oscillator o in voice.Oscillators)
                        o.Reset();
                }
            }
        }

        private void space_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker sampleMaker = this.rowVoices[key];
                if (sampleMaker is Voice)
                {
                    double spaceScale = space.Value / 100;
                    Voice voice = (Voice)sampleMaker;
                    double scaleIncrement = .2;

                    voice.Oscillators[0].Pan = (short)(2.5 * scaleIncrement * spaceScale * short.MaxValue);
                    voice.Oscillators[2].Pan = (short)(5 * scaleIncrement * spaceScale * short.MaxValue);

                    voice.Oscillators[1].Pan = (short)(2.5 * scaleIncrement * spaceScale * short.MinValue);
                    voice.Oscillators[3].Pan = (short)(5 * scaleIncrement * spaceScale * short.MinValue);

                }
            }
        }

        private void sauce_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {

            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker sampleMaker = this.rowVoices[key];
                if (sampleMaker is Voice)
                {
                    double scale = sauce.Value / 100;
                    Voice voice = (Voice)sampleMaker;
                    double scaleIncrement = .333;
                    uint baseValue = 500000;

                    voice.Oscillators[1].PhaseShift = (uint)(scaleIncrement * scale * baseValue);
                    voice.Oscillators[2].PhaseShift = (uint)(2 * scaleIncrement * scale * baseValue);
                    voice.Oscillators[3].PhaseShift = (uint)(3 * scaleIncrement * scale * baseValue);
                }
            }

        }

        private void muck_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker sampleMaker = this.rowVoices[key];
                if (sampleMaker is Voice)
                {
                    double scale = muck.Value / 100;
                    Voice voice = (Voice)sampleMaker;
                    double scaleIncrement = 1;
                    double baseValue = .05;

                    voice.Oscillators[0].PitchBend = -baseValue * scale * scaleIncrement;
                    voice.Oscillators[1].PitchBend = -baseValue * scale * scaleIncrement * .2;
                    voice.Oscillators[2].PitchBend = -baseValue * scale * scaleIncrement * .5;
                    voice.Oscillators[3].PitchBend = -baseValue * scale * scaleIncrement * .7;

                }
            }

        }

        private void delay_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (delay.Value == 0)
                this.sequencer.Delay.Enabled = false;
            else
            {
                this.sequencer.Delay.Enabled = true;

                double scale = delay.Value / 100;
                this.sequencer.Delay.Length = (int)(40000d * scale);
                //this.sequencer.Delay.Length = 40000;
            }
        }

        private void texture_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double value = texture.Value;
            double firstFactor = 1;

            IWaveForm firstWaveForm = null;
            IWaveForm secondWaveForm = null;
            IWaveForm thirdWaveForm = null;
            IWaveForm fourthWaveForm = null;

            if (value > 192)
            {
                firstFactor = 3;
                firstWaveForm = new TriangleWaveForm();
            }
            else if (value > 128)
            {
                firstFactor = 2;
                firstWaveForm = new SquareWaveForm();
            }
            else if (value > 64)
            {
                firstFactor = 1;
                firstWaveForm = new SawWaveForm();
            }
            else
            {
                firstFactor = 0;
                firstWaveForm = new SineWaveForm();
            }


            double secondValue = value - firstFactor * 64;
            double secondFactor = 1;

            if (secondValue > 48)
            {
                secondFactor = 3;
                secondWaveForm = new TriangleWaveForm();

            }
            else if (secondValue > 32)
            {
                secondFactor = 2;
                secondWaveForm = new SquareWaveForm();
            }
            else if (secondValue > 16)
            {
                secondFactor = 1;
                secondWaveForm = new SawWaveForm();
            }
            else
            {
                secondFactor = 0;
                secondWaveForm = new SineWaveForm();
            }

            double thirdValue = secondValue - secondFactor * 16;
            double thirdFactor = 1;

            if (thirdValue > 12)
            {
                thirdFactor = 3;
                thirdWaveForm = new TriangleWaveForm();
            }
            else if (thirdValue > 8)
            {
                thirdFactor = 2;
                thirdWaveForm = new SquareWaveForm();
            }
            else if (thirdValue > 4)
            {
                thirdFactor = 1;
                thirdWaveForm = new SawWaveForm();
            }
            else
            {
                thirdFactor = 0;
                thirdWaveForm = new SineWaveForm();
            }

            double fourthValue = thirdValue - thirdFactor * 4;
            if (fourthValue == 4)
            {
                fourthWaveForm = new TriangleWaveForm();
            }
            else if (fourthValue == 3)
            {
                fourthWaveForm = new SquareWaveForm();
            }
            else if (fourthValue == 2)
            {
                fourthWaveForm = new SawWaveForm();
            }
            else
            {
                fourthWaveForm = new SineWaveForm();
            }

            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker maker = this.rowVoices[key];
                if (maker is Voice)
                {
                    Voice voice = (Voice)maker;
                    voice.Oscillators[0].WaveForm = firstWaveForm;
                    voice.Oscillators[1].WaveForm = secondWaveForm;
                    voice.Oscillators[2].WaveForm = thirdWaveForm;
                    voice.Oscillators[3].WaveForm = fourthWaveForm;
                }
            }

        }
      
        private void attack_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double percent = attack.Value / 100d;
            uint value = (uint)(15000 * percent);
            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker maker = this.rowVoices[key];
                if (maker is Voice)
                {
                    Voice voice = (Voice)maker;
                    voice.Envelope.Attack = value;
                }
            }
        }

        private void release_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double percent = release.Value / 100d;
            uint value = (uint)(15000 * percent);

            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker maker = this.rowVoices[key];
                if (maker is Voice)
                {
                    Voice voice = (Voice)maker;
                    voice.Envelope.Decay = value;
                }
            }
        }

        void LoadDrums()
        {
            ObservableCollection<DrumLibrary> drums = DrumLibrary.GetDrumLibraries();
            this.drumLibraryList.ItemsSource = drums;
            this.drumLibraryList.SelectedIndex = 0;
        }

        private void drumLibraryList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DrumLibrary drums = (DrumLibrary)e.AddedItems[0];

            WavPlayer kick = new WavPlayer();
            kick.WaveData = this.GetResource(drums.KickPath);

            WavPlayer hat = new WavPlayer();
            hat.WaveData = this.GetResource(drums.HatPath);

            WavPlayer snare = new WavPlayer();
            snare.WaveData = this.GetResource(drums.SnarePath);

            WavPlayer tic = new WavPlayer();
            tic.WaveData = this.GetResource(drums.TicPath);

            WavPlayer toc = new WavPlayer();
            toc.WaveData = this.GetResource(drums.TocPath);

            int inc = 0;
            this.CheckAndSetDrumVoice(0, kick);
            inc++;
            this.CheckAndSetDrumVoice(1, snare);
            inc++;
            this.CheckAndSetDrumVoice(2, hat);
            inc++;
            this.CheckAndSetDrumVoice(3, tic);
            inc++;
            this.CheckAndSetDrumVoice(4, toc);

            if (drumGrid.Notes != 6)
            {
                this.drumGrid.Notes = 6;
                this.SetLabels();
            }
        }

        void CheckAndSetDrumVoice(int position, WavPlayer voice)
        {
            if (this.drumRowVoices.ContainsKey(position))
            {
                for (int i = 0; i < this.drumGrid.Steps; i++)
                {
                    this.sequencer.ModifyNote(
                        this.drumRowVoices[position], voice, i);
                }
                this.drumRowVoices[position] = voice;
            }
            else
                this.drumRowVoices.Add(position, voice);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            BitmapImage image = null;
            Uri uri = null;
            if (playing)
            {
                this.media.Stop();
                uri = new Uri("play.png", UriKind.Relative);
                this.playing = false;
                this.sequencer.StepIndex = this.sequencer.StepCount - 1;
            }
            else
            {
                this.media.Play();
                uri = new Uri("stop.png", UriKind.Relative);
                this.playing = true;
            }
            image = new BitmapImage(uri);
            this.playImage.Source = image;
        }

        private void drumLevel_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            foreach (int key in this.drumRowVoices.Keys)
            {
                ISampleMaker maker = this.drumRowVoices[key];
                if (maker is AttenuatorBase)
                {
                    ((AttenuatorBase)maker).Attenuation = (int)this.drumLevel.Value;
                }
            }
        }

        private void synthLevel_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            foreach (int key in this.rowVoices.Keys)
            {
                ISampleMaker maker = this.rowVoices[key];
                if (maker is AttenuatorBase)
                {
                    ((AttenuatorBase)maker).Attenuation = (int)this.synthLevel.Value;
                }
            }

        }

        private void tempoBox_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (this.tempoBox == null) return;
            this.sequencer.Tempo = (int)tempoBox.Value * 4;
        }

        private void stepBox_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (this.stepBox == null) return;
            this.noteGrid.Steps = (int)this.stepBox.Value;
            this.drumGrid.Steps = (int)this.stepBox.Value;
            this.ResetSequencers();
            this.SetLabels();
        }

       
    }
}
